import DataManager from "../common/important/DataManager";
import GameTool from "../common/tool/GameTool";
import ContextComponent from "../common/tool/context/ContextComponent";
import ContextPool from "../common/tool/context/ContextPool";
import { ITouchEvent } from "../expand/interface/ITouchEvent";
import GameWorldSnake from "./GameWorldSnake";
import LocalCommon from "./config/LocalCommon";

const { ccclass, property } = cc._decorator;

@ccclass
export default class GameSnake extends ContextComponent implements ITouchEvent {
    @property(cc.Label)
    public LabelHp: cc.Label = null;
    @property(cc.Label)
    public LabelScore: cc.Label = null;

    @property(cc.Node)
    public NodeBegin: cc.Node = null;

    @property(cc.Prefab)
    public PrefabSankeHead: cc.Prefab = null;
    @property(cc.Prefab)
    public PrefabSankeBody: cc.Prefab = null;

    @property(cc.Node)
    public FollowSnake: Array<cc.Node> = [];


    public get SnakeLength(): number {
        return this.mSnakeBodyArray.length;
    }

    private mIsMove: boolean = true;
    public set IsMove(is: boolean) {
        this.mIsMove = is;
    }
    public get IsMove(): boolean {
        return this.mIsMove;
    }

    protected mSnakeBodyArray: Array<cc.Node> = [];
    protected mSnakeBodyPoint: Array<cc.Vec3> = [];
    protected mTrailResolution: number = LocalCommon.SNAKE_HEIGHT * 0.1;
    protected mSpacing: number = LocalCommon.SNAKE_HEIGHT * 0.75;
    protected mSpacingScale: number = 1;
    protected mHeadDiretionSmoothTime: number = 0.05;

    /**
     * 🐍头
     */
    protected mSnakeHead: cc.Node = null;
    public get SnakeHead(): cc.Node {
        return this.mSnakeHead;
    }
    protected mSnakeHeadRigidBody: cc.RigidBody = null;

    protected mScore: number = 0;
    public get Score(): number {
        return this.mScore;
    }

    public init(content: ContextPool): boolean {
        super.init(content);

        this.mSnakeHead = this.getSankeHead();
        let firstBody = cc.instantiate(this.PrefabSankeHead);
        firstBody.position = this.NodeBegin.position.clone();
        this.node.addChild(firstBody);
        this.mSnakeBodyArray.push(firstBody);
        firstBody.active = false;

        this.mSnakeBodyPoint.push(firstBody.position);
        this.mSnakeBodyPoint.push(this.mSnakeBodyPoint[0]);
        this.refershHp();
        return true;
    }

    public load(): void {

    }

    public getSankeHead(): cc.Node {
        if (!this.mSnakeHead) {
            this.mSnakeHead = cc.instantiate(this.PrefabSankeHead);
            this.mSnakeHeadRigidBody = this.mSnakeHead.getComponent(cc.RigidBody);
            this.node.addChild(this.mSnakeHead);
        }
        return this.mSnakeHead;
    }

    public refershHp(): void {
        this.LabelHp.string = this.SnakeLength + "";
        this.LabelHp.node.x = this.mSnakeHead.x;
        this.LabelHp.node.y = this.mSnakeHead.y + LocalCommon.SNAKE_HEIGHT;
    }

    public refershScore(): void {
        this.LabelScore.string = this.mScore + "";
    }

    public killSnakeBody() {
        if (this.mSnakeBodyArray.length > 1) {
            this.mScore++;

            this.refershScore();

            let body = this.mSnakeBodyArray[this.mSnakeBodyArray.length - 1]
            this.mSnakeBodyArray.splice(this.mSnakeBodyArray.length - 1, 1);
            body.destroy();
        }
        else
            this.snakeDie();
        this.refershHp();
    }

    /**
     * 吃食物
     * @param food 
     */
    public eatFood(food: number) {
        for (let i = 0; i < food; i++) {
            let body = cc.instantiate(this.PrefabSankeBody);
            this.node.addChild(body);
            body.position = this.mSnakeHead.position.clone();

            const colorArr = [
                [0, 0, 0],
                [255, 0, 0],
                [0, 255, 0],
                [0, 0, 255],
                [255, 255, 0],
                [255, 0, 255],
                [0, 255, 255],
                [255, 255, 255]
            ]
            const color = colorArr[Math.floor(Math.random() * colorArr.length)]
            body.color = cc.color(color[0], color[1], color[2]);
            this.mSnakeBodyArray.push(body);
        }
        this.refershHp();
    }

    public sqrMagnitude(value: cc.Vec2): number {
        return (value.x * value.x + value.y * value.y);
    };

    public magnitude(value: cc.Vec2) {
        return Math.sqrt((value.x * value.x) + (value.y * value.y));
    }

    public onUpdate(dt): void {
        if (this.mSnakeBodyArray.length == 0)
            return;

        let speed = this.mSnakeHeadRigidBody.linearVelocity;
        speed = cc.v2(speed.x, LocalCommon.SNAKE_SPEED_Y);
        this.mSnakeHeadRigidBody.linearVelocity = speed;

        if (this.mSnakeBodyPoint.length > 0) {
            this.mSnakeBodyPoint[0] = this.mSnakeHead.position;
            if (this.mSnakeBodyPoint.length === 1)
                this.mSnakeBodyPoint.push(this.mSnakeBodyPoint[0]);
            else {

                let point0 = this.mSnakeBodyPoint[0];
                let point1 = this.mSnakeBodyPoint[1];

                let vector = cc.v2(point0.x - point1.x, point0.y - point1.y);
                if (this.sqrMagnitude(vector) >= (this.mTrailResolution * this.mTrailResolution)) {
                    this.mSnakeBodyPoint.splice(1, 0, this.mSnakeBodyPoint[0]);
                }
            }

            let findNumber = 1;
            let spacing = this.mSpacing;
            let rightNumber = spacing;
            let index = 0;
            while ((index < (this.mSnakeBodyPoint.length - 1)) && (findNumber < this.mSnakeBodyArray.length)) {
                let vectorFirst = this.mSnakeBodyPoint[index];
                let vectorNext = this.mSnakeBodyPoint[index + 1];
                let vectorResult = cc.v2(vectorNext.x - vectorFirst.x, vectorNext.y - vectorFirst.y);
                let magnitude = this.magnitude(vectorResult);
                if (magnitude > 0.0) {
                    let vectorCut = cc.v2(vectorNext.x - vectorFirst.x, vectorNext.y - vectorFirst.y);
                    let normalized = vectorCut.normalize();
                    let vectorBook = cc.v2(vectorFirst.x, vectorFirst.y);

                    let count = this.mSnakeBodyPoint.length;

                    while ((rightNumber <= magnitude) && (findNumber < count)) {
                        let nor = cc.v2(normalized.x * rightNumber, normalized.y * rightNumber);
                        vectorBook.x += nor.x;
                        vectorBook.y += nor.y;
                        magnitude -= rightNumber;

                        let ball = this.mSnakeBodyArray[findNumber];
                        if (ball) {
                            ball.position = cc.v3(vectorBook.x, vectorBook.y);
                        }


                        findNumber++;
                        rightNumber = spacing;

                    }
                    rightNumber -= magnitude;
                }
                index++;
            }

            let vectorOver = this.mSnakeBodyPoint[this.mSnakeBodyPoint.length - 1];
            for (let i = findNumber; i < this.mSnakeBodyArray.length; i++) {
                this.mSnakeBodyArray[findNumber].position = vectorOver;
            }
            index++;
            if (index < this.mSnakeBodyPoint.length) {
                this.mSnakeBodyPoint.splice(index, this.mSnakeBodyPoint.length - index);
            }
        }


        this.refershHp();
        this.refreshFollow();
    }

    public resetY() {
        let speed = this.mSnakeHeadRigidBody.linearVelocity;
        let resert = cc.v2(speed.x, 0);
        this.mSnakeHeadRigidBody.linearVelocity = resert;
    }
    public refreshFollow(): void {
        for (var i = 0; i < this.FollowSnake.length; i++) {
            this.FollowSnake[i].y = this.mSnakeHead.y;
        }
    }

    public snakeDie() {
        DataManager.Score = this.mScore;

        this.mSnakeHeadRigidBody.linearVelocity = cc.v2(0, 0);
        GameWorldSnake.GameOver();
    }

    public revive(): void {
    }

    public onTouchBegin(event: cc.Event.EventTouch): void {
        var touches = event.getTouches();
        var touchpos = touches[0].getLocation();
    }
    public onTouchMove(event: cc.Event.EventTouch): void {
        let touches = event.getTouches();
        let deltax = touches[0].getDelta();
        let velocity = this.mSnakeHeadRigidBody.linearVelocity;
        let speed = cc.v2(deltax.x * 100, velocity.y);
        this.mSnakeHeadRigidBody.linearVelocity = speed;
    }
    public onTouchEnd(event: cc.Event.EventTouch): void {
        let speed = this.mSnakeHeadRigidBody.linearVelocity;
        let resert = cc.v2(0, speed.y);
        this.mSnakeHeadRigidBody.linearVelocity = resert;
    }
    public onTouchCancel(event: cc.Event.EventTouch): void {
    }
}
